package com.ruoyi.network.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.net.NetUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.network.domain.entity.IpRange;
import com.ruoyi.network.model.vo.IpRangeRequestVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author Administrator
 */
@Slf4j
public class IpRangeUtil {

    private static final Pattern IPV4_PATTERN = Pattern.compile("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
    private static final Pattern CIDR_PATTERN = Pattern.compile("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/(3[0-2]|[12]?[0-9])$");

    /**
     * 校验IP段
     *
     * @param vo 网段参数
     */
    public static void isValidIpRangeInfo(IpRangeRequestVO vo) {
        String rangeName = vo.getRangeName();
        int ipMask = vo.getIpMask();

        String rangeKey = ipRangeNamePreCheck(rangeName);
        if (isValidIpv4(rangeKey)) {
            throw new ServiceException("IP段格式不正确");
        }

        vo.setRangeKey(rangeKey);

        ipMaskCheck(ipMask);

        // 校验分配数量是否超出该网段限制
        int maxAllocatableIps = getMaxAllocatableIps(vo.getRangeKey(), ipMask);
        if (vo.getAsgCount() > maxAllocatableIps) {
            throw new ServiceException("分配数量超出该网段限制");
        }
    }

    /**
     * Ip段前置校验
     *
     * @param ipRangeName ip段名称
     * @return String ip段名称
     */
    public static String ipRangeNamePreCheck(String ipRangeName) {
        if (Strings.isBlank(ipRangeName)) {
            throw new ServiceException("IP段不能为空");
        }
        String[] split = ipRangeName.split("\\.");
        if (split.length != 4) {
            if (split.length == 3) {
                return ipRangeName.concat(".0");
            } else {
                throw new ServiceException("IP段格式不正确");
            }
        }
        return ipRangeName;
    }

    /**
     * 子网长度校验
     *
     * @param ipMask 子网长度
     */
    public static void ipMaskCheck(int ipMask) {
        if (ipMask < 0 || ipMask > 32) {
            throw new ServiceException("无效的 子网 长度");
        }
    }

    /**
     * 计算网段中可分配的最大IP数量
     *
     * @param ipRange 网段地址
     * @param ipMask  子网长度
     * @return int 可分配的最大IP数量
     */
    public static int getMaxAllocatableIps(String ipRange, int ipMask) {
//        String concat = ipRange.concat("/" + ipMask);
        int prefixLength = extractPrefixLength(ipMask + "");
        int hostBits = 32 - prefixLength;
        return (1 << hostBits) - 2;
    }

    /**
     * 校验是否是有效的 IPv4 地址
     *
     * @param ip IP 地址
     * @return boolean 是否是有效的 IPv4 地址
     */
    public static boolean isValidIpv4(String ip) {
        return !IPV4_PATTERN.matcher(ip).matches();
    }

    /**
     * 获取 IP 地址所在的网段
     *
     * @param ip   IP 地址
     * @param cidr CIDR 长度
     * @return 网段地址
     */
    public static String getNetworkAddress(String ip, String cidr) {
        if (isValidIpv4(ip)) {
            throw new ServiceException("无效的 IP 地址");
        }

        int prefixLength = extractPrefixLength(cidr);

        String subnetMask = cidrToSubnetMask(prefixLength);

        long ipAddress = NetUtil.ipv4ToLong(ip);
        long mask = NetUtil.ipv4ToLong(subnetMask);

        long networkAddress = ipAddress & mask;
        return NetUtil.longToIpv4(networkAddress);
    }

    /**
     * 提取 子网 长度
     *
     * @param cidr CIDR 表示法
     * @return CIDR 长度
     */
    private static int extractPrefixLength(String cidr) {
        if (Strings.isBlank(cidr)) {
            throw new ServiceException("无效的 CIDR 表示法");
        }
        try {
            if (cidr.startsWith("/")) {
                return Integer.parseInt(cidr.substring(1));
            } else {
                return Integer.parseInt(cidr);
            }
        } catch (Exception e) {
            throw new ServiceException("无效的 CIDR 表示法");
        }
    }

    /**
     * 通过子网长度 计算 子网掩码
     *
     * @param prefixLength 子网 长度
     * @return 子网掩码
     */
    private static String cidrToSubnetMask(int prefixLength) {
        if (prefixLength < 0 || prefixLength > 32) {
            throw new ServiceException("无效的 CIDR 长度");
        }
        int mask = (0xFFFFFFFF << (32 - prefixLength));
        return NetUtil.longToIpv4(mask);
    }

    /**
     * 计算网段起始结束范围
     *
     * @param ipv4         ip地址
     * @param prefixLength 子网长度
     * @return Map<Boolean, String> key:true表示起始IP，false表示结束IP
     */
    public static Map<Boolean, String> calculateIPRange(String ipv4, int prefixLength) {
        InetAddress address;
        try {
            address = InetAddress.getByName(ipv4);
            byte[] addressBytes = address.getAddress();

            int subnetMask = 0xFFFFFFFF << (32 - prefixLength);

            int addressValue = bytesToInt(addressBytes);
            int networkAddress = addressValue & subnetMask;
            int broadcastAddress = networkAddress | (~subnetMask);

            InetAddress startInetAddress = intToInetAddress(networkAddress + 1);
            InetAddress endInetAddress = intToInetAddress(broadcastAddress - 1);

            Map<Boolean, String> map = new HashMap<>(2);
            map.put(true, startInetAddress.getHostAddress());
            map.put(false, endInetAddress.getHostAddress());
            return map;
        } catch (UnknownHostException e) {
            log.error("计算网段范围失败 无效的 IP 地址: {}", ipv4);
        }
        return Collections.emptyMap();
    }

    private static InetAddress intToInetAddress(int value) {
        byte[] bytes = new byte[4];
        for (int i = 0; i < 4; i++) {
            bytes[i] = (byte) ((value >> (8 * (3 - i))) & 0xFF);
        }
        try {
            return InetAddress.getByAddress(bytes);
        } catch (Exception e) {
            throw new BaseException("网段计算错误");
        }
    }

    private static int bytesToInt(byte[] bytes) {
        int value = 0;
        for (byte b : bytes) {
            value = (value << 8) | (b & 0xFF);
        }
        return value;
    }

    /**
     * 数据结构转换 List - Map
     *
     * @param rangeList 网段数集
     * @return Map<Long, IpRange> 转换结果
     */
    public static Map<Long, IpRange> rangeListToMap(List<IpRange> rangeList) {
        if (CollUtil.isEmpty(rangeList)) {
            return Collections.emptyMap();
        }
        return rangeList.stream().collect(Collectors.toMap(IpRange::getStartIp, Function.identity(), (k1, k2) -> k2));
    }

    public static void main(String[] args) throws UnknownHostException {
        String ip = "192.168.18.148";
        String cidr = "/24";
        calculateIPRange(ip, 16);
    }
}
